package com.zkq.graduate.util;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author zhangkaiqiang
 * @Date 2019/10/19  16:09
 * @Description TODO
 *
 * JWT鉴权工具类
 */
public class JwtUtil {


	private static final String JWT_SECERT="nsyedmfiefhsincsjfun";

	public static String createJWT(Map<String, Object> claims, Long time){
		// 指定前面时的签名算法,也就是token的head部分
		SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
		Long nowMillis=System.currentTimeMillis();

		Date now = new Date(nowMillis);
		// 根据字符串得到加密的key
		SecretKey secretKey = generalKey();

		//下面就是在为payload载荷添加各种标准声明和私有声明了
		AtomicInteger atomicInteger=new AtomicInteger();
		JwtBuilder builder = Jwts.builder()
				//如果有私有声明，一定要先设置这个自己创建的私有的声明，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明的
				.setClaims(claims)
				//设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
				.setId(String.valueOf(atomicInteger.getAndIncrement()))
				//iat: jwt的签发时间
				.setIssuedAt(now)
				//设置签名使用的签名算法和签名使用的秘钥
				.signWith(signatureAlgorithm, secretKey);
		if (time >= 0) {
			long expMillis = nowMillis + time;
			Date exp = new Date(expMillis);
			//设置过期时间
			builder.setExpiration(exp);
		}
		return builder.compact();
	}


	/**
	 * 验证jwt
	 */
	public static Map<String, Object> verifyJwt(String token) {
		// 签名秘钥,和生成的签名的秘钥一模一样
		SecretKey key = generalKey();
		Map<String, Object> claims;
		try {
			// 得到DefaultJwtParser
			claims= Jwts.parser()
					// 设置签名的秘钥
					.setSigningKey(key)
				.parseClaimsJws(token).getBody();
		} catch (Exception e) {
			claims = null;
		}
		return claims;
	}

	/**
	 * 根据字符串得到加密的key
	 * @return
	 */
	private static SecretKey generalKey(){
		// Base64.decod()要求传入的参数能被4整除
		byte[] encodedKey = Base64.decode(JWT_SECERT);
		SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
		return key;
	}


	/**
	 * 进行token的刷新
	 * @param request
	 * @param response
	 * @param claim
	 */
	public static void refreshToken(HttpServletRequest request, HttpServletResponse response, Map<String,Object> claim){

		// 刷新token的时间,加入剩余有效时间小于5分钟，进行token的刷新
		int refreshTime=5*60*1000;

		// token的有效期
		int time=2*60*60*1000;
		if(claim!=null){
			int expireTime= (int) claim.get("exp");
			if(expireTime<=refreshTime){
				String token=JwtUtil.createJWT(claim,Long.valueOf(time));
				CookieAndSessionUtil.addCookie("token",token,response,time);
			}
		}
	}
}
